package com.tingyuge.thinking.spring.configuration.bean.scope;

import com.tingyuge.thinking.spring.ioc.overview.domain.User;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Scope;

import java.util.Map;

/**
 * @author xiaodao
 * @version 1.0
 * @date 2021/7/4
 */
public class BeanScopeDemo implements DisposableBean {

    @Bean
    public static User singletonUser(){
        return createUser();
    }

    @Bean
    @Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
    public static User protoTypeUser(){
        return createUser();
    }

    public static User createUser(){
        User user  = new User();
        user.setId(System.nanoTime());
        return user;
    }

    @Autowired
    @Qualifier("singletonUser")
    private User singletonUser;
    @Autowired
    @Qualifier("protoTypeUser")
    private User protoTypeUser;
    @Autowired
    @Qualifier("protoTypeUser")
    private User protoTypeUser1;

    @Autowired
    private Map<String,User> users;

    @Autowired
    private ConfigurableListableBeanFactory beanFactory;   //resolveDependency


    public static void main(String[] args) {
        AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext();
        //注册 configuration class（配置类）--->也是 spring bean
        applicationContext.register(BeanScopeDemo.class);
        //启动应用上下文
        applicationContext.addBeanFactoryPostProcessor(beanFactory -> {
            beanFactory.addBeanPostProcessor(new BeanPostProcessor() {
                @Override
                public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
                    System.out.printf("%s Bean 名称:%s 在初始化之后回调.....%n",bean.getClass().getName(),beanName);
                    return bean;
                }
            });
        });

        applicationContext.refresh();
        //依赖查找  AnnotationDependencyFieldInjectionDemo
        BeanScopeDemo demo = applicationContext.getBean(BeanScopeDemo.class);
        scopeBeanByLookup(applicationContext);
        //singleton Bean 每次依赖注入都是一个对象

        //结论2 :  singleton Bean 和 protoType Bean 均会存在一个
        //protoType Bean 有别于其他地方的依赖注入 prototype bean 都会是新生成的.
        //结论3:
        // 无论是 singleton Bean 还是prototype bean 都会执行初始化
        // prototype bean 不会执行销毁操作

        scopeBeanByInjection(applicationContext);
        System.out.println();

        //显示关闭应用上下文
        applicationContext.close();
    }

    private static void scopeBeanByInjection(AnnotationConfigApplicationContext applicationContext) {
        BeanScopeDemo demo = applicationContext.getBean(BeanScopeDemo.class);
        System.out.println("demo.singletonUser: "+ demo.singletonUser);
        System.out.println("demo.protoTypeUser: "+ demo.protoTypeUser);
        System.out.println("demo.protoTypeUser1: "+ demo.protoTypeUser1);
        System.out.println("demo.singletonUser: "+ demo.singletonUser);
        System.out.println("demo.map: "+ demo.users);
    }

    private static void scopeBeanByLookup(AnnotationConfigApplicationContext applicationContext) {
        for (int i = 0; i <3; i++) {
            //singleton每次都是一样的

            User singletonUser = applicationContext.getBean("singletonUser", User.class);
            System.out.println("singletonUser: "+ singletonUser);
            User protoTypeUser = applicationContext.getBean("protoTypeUser", User.class);
            System.out.println("protoTypeUser: "+ protoTypeUser);
        }


    }

    @Override
    public void destroy() throws Exception {
        System.out.println("当前 BeanScopeDemo 正在销毁中");
        this.protoTypeUser.destory();
        this.protoTypeUser1.destory();
        //获取BeanDefinition
        for (Map.Entry<String,User> entry : users.entrySet()){
            String beanName = entry.getKey();
            BeanDefinition beanDefinition = beanFactory.getBeanDefinition(beanName);
            if(beanDefinition.isPrototype()){//如果当前bean 是 prototype 就销毁
                entry.getValue().destory();
            }
        }
        System.out.println("当前 BeanScopeDemo 正在销毁完成.....");
    }
}
